#include "parammodify.h"
#include "ui_parammodify.h"
#include <QMessageBox>
#include "shadow.h"
#include "msgbox.h"
#include "frminput2019.h"
#include "userlogger.h"
#include "commonfunction.h"

ParamModify::ParamModify(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ParamModify)
{
    ui->setupUi(this);
    bdf = BllDataFlow::getInstance();
    sqlHelper = SqlHelper::getInstance();

    //Signals and Slots
    connect(bdf, &BllDataFlow::sendModifyParameterSignal, this, &ParamModify::setParameter);
    connect(bdf, &BllDataFlow::retranslateUiSignal, this, &ParamModify::retranslateUiSlot);

    initWidget();
}

ParamModify::~ParamModify()
{
    delete ui;
}

void ParamModify::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.fillRect(this->rect(), QColor(255, 255, 255, 0));
}

void ParamModify::showEvent(QShowEvent *)
{
    this->activateWindow();
}

void ParamModify::setParameter(const Parameter &parameter)
{
    disconnectSignals();

    paramBefore = parameter;
    paramAfter = parameter;
    ui->lineName->setText(paramAfter.name);
    ui->lineName->setEnabled(false);    //name not editable
    ui->lineName->setProperty("noinput",true);//不需要虚拟键盘
    ui->lineCode->setText(paramAfter.code);
    ui->spinVolt->setValue(paramAfter.volt);
    ui->spinPulse->setValue(paramAfter.pulse);
    ui->spinInterval->setValue(paramAfter.interval);
    ui->spinSpeed->setValue(paramAfter.speed);
    ui->spinTimes->setValue(paramAfter.times);

    ui->btnIsOn->setChecked(paramAfter.ison);
    if (paramAfter.ison)
    {
        ui->btnIsOn->setText(QString(tr("Enabled")));
        ui->btnIsOn->setStyleSheet(NormalBtnEnableCss);
    }
    else
    {
        ui->btnIsOn->setText(QString(tr("Disabled")));
        ui->btnIsOn->setStyleSheet(NormalBtnDisEnableCss);
    }

    connectSignals();
}


static bool parameterEquals(const Parameter &paramBefore, const Parameter &paramAfter)
{
    return (paramAfter.name == paramBefore.name)
        && (paramAfter.code == paramBefore.code)
        && (doubleEquals(paramAfter.volt, paramBefore.volt))
        && (doubleEquals(paramAfter.pulse, paramBefore.pulse))
        && (doubleEquals(paramAfter.interval, paramBefore.interval))
        && (doubleEquals(paramAfter.speed, paramBefore.speed))
        && (doubleEquals(paramAfter.times,  paramBefore.times))
        && (paramAfter.ison == paramBefore.ison);
}

static void parameterModifyLog(const Parameter &paramBefore, const Parameter &paramAfter)
{
    QString beforeStr, afterStr;

    beforeStr = QString(">%1: %2(%3)%4: %5V, %6: %7μs, %8: %9ml/min, %10: %11ms, %12: %13, %14")
                    .arg(QObject::tr("Before Modify")).arg(paramBefore.name).arg(paramBefore.code)
                    .arg(QObject::tr("Voltage")).arg(paramBefore.volt)
                    .arg(QObject::tr("Pulse")).arg(paramBefore.pulse)
                    .arg(QObject::tr("Flow Rate")).arg(paramBefore.speed)
                    .arg(QObject::tr("Interval")).arg(paramBefore.interval)
                    .arg(QObject::tr("Times")).arg(paramBefore.times)
                    .arg(paramBefore.ison ? QObject::tr("On") : QObject::tr("Off"));

    afterStr = QString(">%1: %2(%3), %4: %5V, %6: %7μs, %8: %9ml/min, %10: %11ms, %12: %13, %14")
                    .arg(QObject::tr("After Modify")).arg(paramAfter.name).arg(paramAfter.code)
                    .arg(QObject::tr("Voltage")).arg(paramAfter.volt)
                    .arg(QObject::tr("Pulse")).arg(paramAfter.pulse)
                    .arg(QObject::tr("Flow Rate")).arg(paramAfter.speed)
                    .arg(QObject::tr("Interval")).arg(paramAfter.interval)
                    .arg(QObject::tr("Times")).arg(paramAfter.times)
                    .arg(paramAfter.ison ? QObject::tr("On") : QObject::tr("Off"));

    UserLogger::log(QString("%1: %2\n%3\n%4").arg(QObject::tr("Modify parameters")).arg(paramBefore.name).arg(beforeStr).arg(afterStr));
}

void ParamModify::on_btnCancel_clicked()
{
    this->hide();
}

static void focusOn(QLineEdit *lineEdit)
{
    if (lineEdit)
    {
        lineEdit->selectAll();
        lineEdit->setFocus();
    }
}

void ParamModify::autoFocusOn()
{
    // 焦点转换表
    const QMap<QLineEdit *, QLineEdit *> mapper = {
        {ui->lineName, ui->lineCode},
        {ui->lineCode, ui->spinVolt->findChild<QLineEdit *>()},
        {ui->spinVolt->findChild<QLineEdit *>(), ui->spinPulse->findChild<QLineEdit *>()},
        {ui->spinPulse->findChild<QLineEdit *>(), ui->spinSpeed->findChild<QLineEdit *>()},
        {ui->spinSpeed->findChild<QLineEdit *>(), ui->spinInterval->findChild<QLineEdit *>()},
        {ui->spinInterval->findChild<QLineEdit *>(), ui->spinTimes->findChild<QLineEdit *>()},
        {ui->spinTimes->findChild<QLineEdit *>(), Q_NULLPTR}
    };

    QLineEdit *lineEdit = qobject_cast<QLineEdit *>(QObject::sender());
    if (lineEdit)
    {
        lineEdit->deselect();
        Q_ASSERT(mapper.contains(lineEdit));
        focusOn(mapper[lineEdit]);
    }
}

void ParamModify::on_btnConfirm_clicked()
{
    frmInput2019::Instance()->hideAndClearFocus();

    paramAfter.name = ui->lineName->text().trimmed();
    paramAfter.code = ui->lineCode->text().trimmed();
    paramAfter.volt = ui->spinVolt->value();
    paramAfter.pulse = ui->spinPulse->value();
    paramAfter.interval = ui->spinInterval->value();
    paramAfter.speed = ui->spinSpeed->value();
    paramAfter.times = ui->spinTimes->value();
    paramAfter.ison = ui->btnIsOn->isChecked();

    if (paramAfter.code.isEmpty())
    {
        MsgBox::information(this, tr("Parameter"), tr("Parameter code cannot be empty!"));
        focusOn(ui->lineCode);
        return;
    }

    if (doubleEquals(paramAfter.volt, 0))
    {
        MsgBox::warning(this, tr("Parameter"), tr("Volt shouldn't be zero."));
        return;
    }

    if (doubleEquals(paramAfter.speed, 0))
    {
        MsgBox::warning(this, tr("Parameter"), tr("Speed shouldn't be zero."));
        return;
    }

    if (doubleEquals(paramAfter.pulse, 0))
    {
        MsgBox::warning(this, tr("Parameter"), tr("Pulse shouldn't be zero."));
        return;
    }

    if (doubleEquals(paramAfter.interval, 0))
    {
        MsgBox::warning(this, tr("Parameter"), tr("Interval shouldn't be zero."));
        return;
    }

    if (doubleEquals(paramAfter.times, 0))
    {
        MsgBox::warning(this, tr("Parameter"), tr("Times shouldn't be zero."));
        return;
    }

    if (parameterEquals(paramBefore, paramAfter))
    {// 参数未修改，不做提示
        this->hide();
        return;
    }

    parameterModifyLog(paramBefore, paramAfter);

    EttaError res = sqlHelper->updateParameterBasedOnName(paramAfter);
    if (EttaError::OK == res)
    {
        QString message = tr("Succeed to modify parameter");
        UserLogger::log(QString("%1: %2").arg(message).arg(paramAfter.name));
        MsgBox::information(this, tr("Parameter"), message);
        bdf->showAllParameters();
        this->hide();
    }
    else
    {
        QString message = tr("Fail to modify parameter");
        UserLogger::log(QString("%1: %2").arg(message).arg(paramAfter.name));
        MsgBox::warning(this, tr("Parameter"), message);
    }
}

void ParamModify::on_btnIsOn_toggled(bool checked)
{
    if (checked)
    {
        QList<QStringList> info = sqlHelper->parameterEnableCount();
        int enableCount = info.size();
        bool alreadyEnable = false;
        for (int i = 0; i < info.size(); i++)
        {
            Parameter enableParam = sqlHelper->convertSL2Parameter(info.at(i));
            if (enableParam.code == paramAfter.code)
            {
                alreadyEnable = true;
            }
        }
        if (enableCount >= MAX_PARAMETER_COUNT && !paramAfter.ison && !alreadyEnable)
        {
            paramAfter.ison = false;
            ui->btnIsOn->setChecked(false);
            MsgBox::information(this,
                                     QString(tr("ParameterModify")),
                                     QString(tr("MAX_PARAMETER_COUNT = %1!")).arg(MAX_PARAMETER_COUNT));
        }
        else
        {
            paramAfter.ison = true;
            ui->btnIsOn->setText(QString(tr("Enabled")));
            ui->btnIsOn->setStyleSheet(NormalBtnEnableCss);
        }
    }
    else
    {
        paramAfter.ison = false;
        ui->btnIsOn->setText(QString(tr("Disabled")));
        ui->btnIsOn->setStyleSheet(NormalBtnDisEnableCss);
    }

}

void ParamModify::on_btnClose_clicked()
{
    this->hide();
}

void ParamModify::initWidget()
{
    setAttribute(Qt::WA_TranslucentBackground, true);
    QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect();
    shadow->setColor(QColor(0,0,0,150));
    shadow->setOffset(1, 1);
    shadow->setBlurRadius(16);
    ui->widget->setGraphicsEffect(shadow);
    setWindowFlags(windowFlags() | Qt::SubWindow);

    foreach (const QSpinBox *spinBox, findChildren<QSpinBox *>())
    {
        spinBox->findChild<QLineEdit *>()->setPlaceholderText(QString("%1-%2").arg(spinBox->minimum()).arg(spinBox->maximum()));
    }

    foreach (const QDoubleSpinBox *spinBox, findChildren<QDoubleSpinBox *>())
    {
        spinBox->findChild<QLineEdit *>()->setPlaceholderText(QString("%1-%2").arg(spinBox->minimum()).arg(spinBox->maximum()));
    }

    connectSignals();
}

void ParamModify::connectSignals()
{
    connect(ui->spinInterval, SIGNAL(valueChanged(int)), this, SLOT(onUpdateParamTimes()));
    connect(ui->spinPulse, SIGNAL(valueChanged(int)), this, SLOT(onUpdateParamInterval()));
    connect(ui->spinTimes, SIGNAL(valueChanged(double)), this, SLOT(onUpdateParamInterval()));
    connect(ui->spinSpeed, SIGNAL(valueChanged(double)), this, SLOT(onUpdateParamInterval()));

    connect(ui->spinVolt->findChild<QLineEdit *>(), SIGNAL(returnPressed()), this, SLOT(onSpinBoxReturnPressed()));
    connect(ui->spinPulse->findChild<QLineEdit *>(), SIGNAL(returnPressed()), this, SLOT(onSpinBoxReturnPressed()));
    connect(ui->spinSpeed->findChild<QLineEdit *>(), SIGNAL(returnPressed()), this, SLOT(onSpinBoxReturnPressed()));
    connect(ui->spinInterval->findChild<QLineEdit *>(), SIGNAL(returnPressed()), this, SLOT(onSpinBoxReturnPressed()));
    connect(ui->spinTimes->findChild<QLineEdit *>(), SIGNAL(returnPressed()), this, SLOT(onSpinBoxReturnPressed()));
}

void ParamModify::disconnectSignals()
{
    disconnect(ui->spinInterval, SIGNAL(valueChanged(int)), this, SLOT(onUpdateParamTimes()));
    disconnect(ui->spinPulse, SIGNAL(valueChanged(int)), this, SLOT(onUpdateParamInterval()));
    disconnect(ui->spinTimes, SIGNAL(valueChanged(double)), this, SLOT(onUpdateParamInterval()));
    disconnect(ui->spinSpeed, SIGNAL(valueChanged(double)), this, SLOT(onUpdateParamInterval()));

    disconnect(ui->spinVolt->findChild<QLineEdit *>(), SIGNAL(returnPressed()), this, SLOT(onSpinBoxReturnPressed()));
    disconnect(ui->spinPulse->findChild<QLineEdit *>(), SIGNAL(returnPressed()), this, SLOT(onSpinBoxReturnPressed()));
    disconnect(ui->spinSpeed->findChild<QLineEdit *>(), SIGNAL(returnPressed()), this, SLOT(onSpinBoxReturnPressed()));
    disconnect(ui->spinInterval->findChild<QLineEdit *>(), SIGNAL(returnPressed()), this, SLOT(onSpinBoxReturnPressed()));
    disconnect(ui->spinTimes->findChild<QLineEdit *>(), SIGNAL(returnPressed()), this, SLOT(onSpinBoxReturnPressed()));
}

void ParamModify::on_lineCode_returnPressed()
{
    paramAfter.code = ui->lineCode->text().trimmed();
    if(paramAfter.code.isEmpty())
    {
        MsgBox::information(this, tr("Parameter"), QString(tr("Parameter code cannot be empty!")));
        focusOn(ui->lineCode);
        return;
    }

    autoFocusOn();
}

void ParamModify::onSpinBoxReturnPressed()
{
    autoFocusOn();
}

void ParamModify::onUpdateParamTimes()
{
    disconnectSignals();

    paramAfter.pulse = ui->spinPulse->value();
    paramAfter.speed = ui->spinSpeed->value();
    paramAfter.interval = ui->spinInterval->value();

    if (doubleGreater(paramAfter.pulse, 0) && doubleGreater(paramAfter.speed, 0) && doubleGreater(paramAfter.interval, 0))
    {
        paramAfter.times = PARAM_VOLUME  / ((paramAfter.pulse + paramAfter.interval * 1000) * paramAfter.speed / 60000);
        ui->spinTimes->setValue(paramAfter.times);
    }

    connectSignals();
}

void ParamModify::onUpdateParamInterval()
{
    disconnectSignals();

    paramAfter.pulse = ui->spinPulse->value();
    paramAfter.speed = ui->spinSpeed->value();
    paramAfter.times = ui->spinTimes->value();

    if (doubleGreater(paramAfter.pulse, 0) && doubleGreater(paramAfter.speed, 0) && doubleGreater(paramAfter.times, 0))
    {
        paramAfter.interval = (PARAM_VOLUME * 60) / (paramAfter.speed * paramAfter.times) - paramAfter.pulse / 1000;
        ui->spinInterval->setValue(paramAfter.interval);
    }

    connectSignals();
}

void ParamModify::retranslateUiSlot()
{
    ui->retranslateUi(this);
}
